package tree;

import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;

import model.Data;
import model.Stock;
import pools.PoolsManager;
import tree.nodeType.BooleanFunction;



public class Tree {
	private int maxDepth;
	private Node head;
	private TreeNodesManager treeNodesManager;
	private double fitness;
	

	public Tree(Node head, TreeNodesManager treeNodesManager,int maxDepth,double fitness){
		this.head=head;
		this.treeNodesManager=treeNodesManager;
		this.maxDepth=maxDepth;
		this.fitness=fitness;
		
	}
	public Tree(int maximumDepht) {
		this.maxDepth=maximumDepht;
		this.head=null;
		this.fitness =0;
		this.treeNodesManager = new TreeNodesManager();
 
	}
	
	public Node getHead() {
		return head;
	}
	
	
	public void randomInit(){		
		//head=PoolsManager.get().getRandomNumericFuncNode(0, maxDepth, null,treeNodesManager);
		head=PoolsManager.get().getRandomBooleanNode(0, maxDepth, null,treeNodesManager);
		//System.out.println(this);
	}
	
	public ArrayList<Node> getAllNodesByType(int type){
		ArrayList<Node>answer=null;
		switch(type){
			case 0:{
				answer=treeNodesManager.getAllConstantsInTree();				
				break;
			}
			case 1:{
				answer=treeNodesManager.getAllNumericFunctionsInTree();				
				break;
			}
			case 2:{
				answer=treeNodesManager.getAllBooleansFunctionInTree();
				break;
			}
			case 3:{
				answer=treeNodesManager.getAllBooleanNumericFunctionInTree();
				break;
			}
			default:{}
		}
		return answer;
	}
	public ArrayList<Node> getAllNodesInTree() {
		return treeNodesManager.getAllNodesInTree();
	}
	
	public void agressiveMutation(){
		
	}
	
	public void gentleMutation(){
		
	}
	

	
	public boolean getAdvice(){
		return ((BooleanFunction)head).execute();
	}
	
	public void calcFitness(){

		double profit = 0;
		if (head!=null){
			for ( Stock s: Data.get().getAllStocks() ){
				Data.get().setCurrentStock(s);
				boolean ans=false;				
				 ans = getAdvice();				
				if (ans==true){	
					profit = profit + s.getProfit(); 
				}else{
					if (s.getProfit()>0){ 	//the tree was badly wrong
						profit = profit - (2*s.getProfit()); 
					}else{					//the tree was right - there is no profit
						profit = profit + (-1*s.getProfit());
					}
				}
			}
		}
		if (profit<0){
			profit = 0;
		}
		this.fitness = profit;
	}
	
	public double getFitness() {
		return fitness;
	}
	
	public void saveTreeToFile(){
		XMLEncoder encoder=null;
		try {
			encoder= new XMLEncoder(new BufferedOutputStream(new FileOutputStream(Data.get().getModelfile()+".xml")));
			encoder.writeObject(this);
			
		} catch (FileNotFoundException e) {			
			e.printStackTrace();
		}finally{
			if (encoder!=null){
				encoder.close();
			}
		}
	}
	
	@Override
	public String toString() {	
		return head.printSubTree(0);
	}
	
	//-------------------------------Functions needed for XML file---------------------//	
	public Tree(){}
	public int getMaxDepth() {
		return maxDepth;
	}
	//public void setDepth(int depth) {
	//	this.depth = depth;
	//}
	public void setFitness(double fitness) {
		this.fitness = fitness;
	}
	public void setHead(Node head) {
		this.head = head;
	}
	public void setMaxDepth(int maxDepth) {
		this.maxDepth = maxDepth;
	}
	public TreeNodesManager getTreeNodesManager() {
		return treeNodesManager;
	}
	public void setTreeNodesManager(TreeNodesManager treeNodesManager) {
		this.treeNodesManager = treeNodesManager;
	}
	//------------------------------------------------------------------------------//

	public void replaceNode(Node chosenNode, Node secondChosenNode) {
		//System.out.println(this);
		chosenNode.replace(secondChosenNode,treeNodesManager);
		
	}
	public int getDepth(){
		return recursiveDepthSearch(head);
	}
	
	private int recursiveDepthSearch(Node currentNode){
		int answer=currentNode.getDepth();
		int pathMaxDepth=0;
		for (Node son : currentNode.getSons()){
			pathMaxDepth=recursiveDepthSearch(son);
			if (answer<pathMaxDepth){
				answer=pathMaxDepth;
			}
			
		}
		return answer;
	}
	public void replaceSubTree(Node chosenNode,Node secondChosenNode){		
		chosenNode.replaceSubTree(secondChosenNode,treeNodesManager);
	}
	public Tree cloneTree(){
		TreeNodesManager treeNodesManager=new TreeNodesManager();
		return new Tree(head.cloneSubTree(treeNodesManager,null),treeNodesManager,this.maxDepth,this.fitness);
	}
}
